package org.particles.domain.sim;

import java.util.Arrays;
import java.util.List;

import org.particles.common.CommonUtil;
import org.particles.domain.field.RejectionForce;
import org.particles.domain.world.CompositeOutOfBoundResolver;
import org.particles.domain.world.FieldRepository;
import org.particles.domain.world.GravitationOobr;
import org.particles.domain.world.OutOfWorldBoundResolver;
import org.particles.domain.world.RicochetOutOfBoundResolver;
import org.particles.domain.world.World;
import org.particles.domain.world.World2D;



public class MoleculeSimulation extends StringSimulation {
	public static final int PARAM_GRAVIT = CommonUtil.uniqueIndex();
	public static final int PARAM_CORE_REJ = CommonUtil.uniqueIndex();
	private final GravitationOobr gravitationOobr = new GravitationOobr(
			GravitationOobr.SIDE_SOUTH, 0.02);
	private final RejectionForce coreRejForce;

	public MoleculeSimulation() {
		super();
		setName("Chemistry");
		setNumberParticles(100);
		setParticleRadius(3);
		setMaxParticleImpulse(6);
		
		stringForce.setRejStrength(1);
		stringForce.setAttrStrength(0.5);
		stringForce.setMaxLength(10);
		stringForce.setMinLength(20);
		
		setFieldMarkers(getFieldMarkers() | FieldRepository.REJECT_LIN);
		coreRejForce = (RejectionForce) getFieldRepository().getField(
				FieldRepository.REJECT_LIN);
		coreRejForce.setStrength(5);
		coreRejForce.getDistanceFunc().setFactor(getParticleRadius() * 2);
	}
	
	@Override
	public World createWorld() {
		final OutOfWorldBoundResolver resolver = new CompositeOutOfBoundResolver(
				Arrays.asList(new OutOfWorldBoundResolver[] {
						gravitationOobr,
						new RicochetOutOfBoundResolver(),
				}));
		final World2D world = (World2D) super.createWorld();
		world.setOutOfBoundResolver(resolver);
		return world;
	}

	@Override
	public boolean changeParam(int paramCode, double newValue) {
		if(super.changeParam(paramCode, newValue)) {
			return true;
		}
		else {
			if(PARAM_GRAVIT == paramCode) {
				this.gravitationOobr.setAccelerationRate(newValue);
				return true;
			}
			if(PARAM_CORE_REJ == paramCode) {
				coreRejForce.setStrength(newValue);
				return true;
			}
			else {
				return false;
			}
		}
	}

	@Override
	public List<SimParam> availableParams() {
		final List<SimParam> params = super.availableParams();
		SimParam param;
		param = new SimParam(
				PARAM_GRAVIT, 
				"gravitation strength", 
				gravitationOobr.getAccelerationRate());
		param.setMinValue(0D);
		param.setMaxValue(100D);
		params.add(param);
		param = new SimParam(
				PARAM_CORE_REJ, 
				"core strength", 
				coreRejForce.getStrength());
		param.setDesc("rejection strength of core of a particle");
		param.setMinValue(0D);
		param.setMaxValue(100D);
		params.add(param);
		return params;
	}
}
